﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;

namespace Pittypat.Web
{
    /// <summary>
    /// 表示一个发送给客户端的 JSON 格式的消息。
    /// </summary>
    abstract class Message : Model
    {
        /// <summary>
        /// 创建一个空消息。
        /// </summary>
        /// <returns>新创建的消息对象。</returns>
        internal static Message Create()
        {
            return new EmptyMessage();
        }

        /// <summary>
        /// 创建一个错误消息或空消息。
        /// </summary>
        /// <param name="error">错误消息文本。</param>
        /// <returns>新创建的消息对象，如果 error 为空引用或空字符串，则返回一个空消息，而不是错误消息。</returns>
        internal static Message Create(string error)
        {
            return string.IsNullOrEmpty(error) ? (Message)new EmptyMessage() : new ErrorMessage(error);
        }

        /// <summary>
        /// 创建一个异常错误消息或空消息。
        /// </summary>
        /// <param name="exc">异常对象。</param>
        /// <returns>新创建的错误消息对象，如果 exc 为空引用或空字符串，则返回一个空消息，而不是错误消息。</returns>
        internal static Message Create(Exception exc)
        {
            return exc == null ? (Message)new EmptyMessage() : new ErrorMessage(exc);
        }

        /// <summary>
        /// 创建一个值消息或空消息。
        /// </summary>
        /// <param name="value">消息的值。</param>
        /// <returns>新创建的值消息对象，如果 value 为空引用，则返回一个空消息。</returns>
        internal static Message Create(IJson value)
        {
            if (value == null)
            {
                return new EmptyMessage();
            }

            if (value is VirtualFile)
            {
                return new VirtualFileMessage(value as VirtualFile);
            }

            if (value is AsyncStream)
            {
                return new AsyncStreamMessage(value as AsyncStream);
            }

            if (value is IUser)
            {
                return new UserMessage(value as IUser);
            }

            if (value is VirtualStream)
            {
                return new VirtualStreamMessage(value as VirtualStream);
            }

            if (value is ByteArray)
            {
                return new ByteArrayMessage(value as ByteArray);
            }

            return new ValueMessage(value);
        }

        /// <summary>
        /// 创建一个进度消息。
        /// </summary>
        /// <param name="percent">当前进度的百分比。</param>
        /// <param name="message">当前进度下的消息。</param>
        /// <param name="value">同时发送给远程终端的值。</param>
        /// <returns>新创建的进度消息对象。</returns>
        internal static Message Create(float percent, string message, IJson value)
        {
            return new ProgressMessage(percent, message, value);
        }

        /// <summary>
        /// 创建一个用户信息消息。
        /// </summary>
        /// <param name="user">用户对象。</param>
        /// <returns>新创建的消息对象，如果 user 为空引用，则返回一个匿名用户的消息。</returns>
        internal static Message Create(IUser user)
        {
            return new UserMessage(user);
        }

        /// <summary>
        /// 使用状态代码和描述初始化 <see cref="StatusMessage"/> 类的新实例。
        /// </summary>
        /// <param name="status">状态代码。</param>
        /// <param name="description">描述信息。</param>
        internal static Message Create(HttpStatusCode status, string description = null)
        {
            return new StatusMessage(status, description);
        }

        private List<CookieHeaderValue> cookies;

        /// <summary>
        /// 初始化 <see cref="Message"/> 类的新实例。
        /// </summary>
        internal Message()
        {
            this.cookies = new List<CookieHeaderValue>(4);
        }

        /// <summary>
        /// 获取要发送到客户端的 COOKIE 的列表。
        /// </summary>
        internal List<CookieHeaderValue> Cookies
        {
            get
            {
                return this.cookies;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示当前消息是否是空消息。
        /// </summary>
        internal bool IsEmpty
        {
            get
            {
                return this.GetType() == typeof(EmptyMessage);
            }
        }

        const int DefaultBufferSize = 8192;

        /// <summary>
        /// 通过 WebSocket 发送消息。
        /// </summary>
        /// <param name="progress">异步进度。</param>
        /// <param name="bufferSize">缓冲区的大小。</param>
        /// <returns>异步任务。</returns>
        internal virtual Task SendAsync(WebSocketAsyncProgress progress, int bufferSize = 0)
        {
            bufferSize = bufferSize <= 0 ? DefaultBufferSize : bufferSize;

            using (var stream = new MemoryStream(bufferSize))
            {
                using (var writer = new StreamWriter(stream, Encoding.UTF8))
                {
                    this.Write(writer);
                    writer.Flush();

                    var buf = stream.GetBuffer();
                    var len = (int)stream.Length;

                    if (len >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
                    {
                        // 去掉 utf-8 的 BOM
                        return progress.Socket.SendAsync(new ArraySegment<byte>(buf, 3, len - 3), WebSocketMessageType.Text, true, progress.Token);
                    }

                    return progress.Socket.SendAsync(new ArraySegment<byte>(buf, 0, len), WebSocketMessageType.Text, true, progress.Token);
                }
            }
        }

        /// <summary>
        /// 将文件作为响应内容附加到指定的响应消息。
        /// </summary>
        /// <param name="response">目标响应。</param>
        internal virtual void ToResponseContent(HttpResponseMessage response)
        {
            using (var writer = new StringWriter())
            {
                this.Write(writer);
                response.Content = new StringContent(writer.ToString(), Encoding.UTF8, "application/json");
            }
        }
    }
}
